package com.tbit.uqbike.service.business.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import com.tbit.uqbike.constant.RefundLogConstant;
import com.tbit.uqbike.object.pojo.*;
import com.tbit.uqbike.object.pojo.dto.*;
import com.tbit.uqbike.object.pojo.vo.*;
import com.tbit.uqbike.object.value.LoginInfo;
import com.tbit.uqbike.service.base.TokenService;
import com.tbit.uqbike.service.business.*;
import com.tbit.uqbike.webmanager.controller.BaseController;
import com.tbit.uqbike.webmanager.dao.capital.BadOrderDao;
import com.tbit.uqbike.webmanager.dao.capital.RefundLogDao;
import com.tbit.uqbike.webmanager.dao.capital.UserCapitalChangeDao;
import com.tbit.uqbike.webmanager.dao.core.AccountDao;
import com.tbit.uqbike.webmanager.dao.stat.AccountDayStatDao;
import com.tbit.manager.api.dto.OperationalDataDTO;
import com.tbit.uqbike.webmanager.util.*;
import com.tbit.uqbike.webmanager.util.csv.CsvExportUtils;
import com.tbit.uqbike.webmanager.util.csv.entity.CsvBaseEntity;
import com.tbit.uqbike.webmanager.util.csv.entity.ExportHandler;
import com.tbit.uqbike.webmanager.util.excel.ExportExcelUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @AUTHOR: wei.xing
 * @DATE: 2022-12-06 20:26
 **/
@Service
public class DownloadAllStatServiceImpl implements DownloadAllStatService {

    @Autowired
    DepositStatService depositStatService;
    @Autowired
    TransactionOrderStatService transactionOrderStatService;
    @Autowired
    AdvanceDepositStatService advanceDepositStatService;
    @Autowired
    RefundLogService refundLogService;
    @Autowired
    PayCardLogService payCardLogService;
    @Autowired
    RideLogDetailsService rideLogDetailsService;

    @Autowired
    ArrearsLogService arrearsLogService;
    @Autowired
    GiftCardStatService giftCardStatService;
    @Autowired
    RechargeGiveStatService rechargeGiveStatService;

    @Autowired
    AccountDayStatDao accountDayStatDao;
    @Autowired
    AccountDao accountDao;
    @Autowired
    BadOrderDao badOrderDao;

    @Autowired
    UserCapitalChangeDao changeDao;

    @Autowired
    CsvExportUtils csvExportUtils;
    @Autowired
    AccountDayStatService accountDayStatService;

    @Autowired
    RefundLogDao refundLogDao;

    @Autowired
    TokenService tokenService;

    @Override
    public String downloadByOperational(DownloadAllStatDetailDTO downloadDTO) {
        downloadDTO.setEndTime(DateUtils.endOfDay(downloadDTO.getEndTime()));
        OperationalDataDTO operationalDataDTO = BeanUtils.copyProperties(downloadDTO, OperationalDataDTO.class);
        Map<String, Object> operationalDataMap = accountDayStatService.operationalData(operationalDataDTO);
        OperationalData operationalData = new OperationalData(operationalDataMap);

        File tempFile = csvExportUtils.getTempFile();
        try {
            String timeName = "-" + DateUtil.formatDate(downloadDTO.getStartTime()) + "-" + DateUtil.formatDate(downloadDTO.getEndTime());
            String fileName = "总计表（选择所有区域）";
            if (downloadDTO.getIsAllAccount() == 2) {
                fileName = "总计表（未选择所有区域）";
            }
            File file = new File(tempFile, fileName + ".xls");

            List<String[]> rows = new ArrayList<>();

            if (downloadDTO.getIsAllAccount() == 1) {
                rows.add(new String[]{"商户统计(公式=运营营收+预存款+押金+其他收支)", operationalData.getMerchantStat().toString()});
                rows.add(new String[]{"运营营收", operationalData.getOperation().toString()});
                rows.add(new String[]{"预存款", operationalData.getAdvanceDeposit().toString()});
                rows.add(new String[]{"押金", operationalData.getUserDeposit().toString()});
                rows.add(new String[]{"其他收支", operationalData.getOther().toString()});
            }
            rows.add(new String[]{""});
            rows.add(new String[]{"商户统计(公式=微信商户+支付宝商户+汇付商户)", operationalData.getMerchant().toString()});
            rows.add(new String[]{"微信商户（商户交易订单明细对账单）", operationalData.getWechat().toString()});
            rows.add(new String[]{"支付宝商户（商户交易订单明细对账单）", operationalData.getAlipay().toString()});
            rows.add(new String[]{"汇付商户（商户交易订单明细对账单）", operationalData.getRemittance().toString()});
            rows.add(new String[]{""});
            rows.add(new String[]{"运营营收(公式=骑行费用+用户罚款+用户购卡-退款)", operationalData.getOperation().toString()});
            rows.add(new String[]{"骑行费用（骑行订单明细对账单）", operationalData.getCyclingIncome().toString()});
            rows.add(new String[]{"用户罚款（骑行订单明细对账单）", operationalData.getUserFineIncome().toString()});
            rows.add(new String[]{"用户购卡（购卡订单明细对账单）", operationalData.getPurchaseIncome().toString()});
            rows.add(new String[]{"退款（退款订单明细对账单）", operationalData.getRefund().toString()});
            rows.add(new String[]{""});
            rows.add(new String[]{"预存款", operationalData.getAdvanceDeposit().toString()});
            rows.add(new String[]{"预存款交易收入（预存款收支明细对账单）", operationalData.getAdvanceDepositIncome().toString()});
            rows.add(new String[]{"预存款交易支出（预存款收支明细对账单）", operationalData.getAdvanceDepositExpenses().toString()});
            rows.add(new String[]{""});
            rows.add(new String[]{"押金", operationalData.getUserDeposit().toString()});
            rows.add(new String[]{"押金收入（押金收支明细对账单）", operationalData.getUserDepositIncome().toString()});
            rows.add(new String[]{"押金支出（押金收支明细对账单）", operationalData.getUserDepositExpenses().toString()});
            rows.add(new String[]{""});
            rows.add(new String[]{"其他收支", operationalData.getOther().toString()});
            rows.add(new String[]{"其他收入", operationalData.getOtherIncome().toString()});
            rows.add(new String[]{"其他支出", operationalData.getOtherExpenses().toString()});
            rows.add(new String[]{""});

            if (downloadDTO.getIsAllAccount() == 1) {
                rows.add(new String[]{"注：预存款收支不包含手工修改预存款金额"});
            } else {
                rows.add(new String[]{"注1：未勾选所有区域，商户统计≠运营营收+预存款+押金+其他收支"});
                rows.add(new String[]{"注2：预存款收支不包含手工修改预存款金额"});
            }

            ExportExcelUtil.exportList(fileName, rows, file);

            DepositIncomeDownloadDTO depositIncomeDownloadDTO = BeanUtils.copyProperties(downloadDTO, DepositIncomeDownloadDTO.class);
            depositIncomeDownloadDTO.setFileName("押金收支明细" + timeName);
            File depositFile = depositStatService.exportIncome(depositIncomeDownloadDTO, tempFile);

            TransactionOrderIncomeDownloadDTO transactionOrderIncomeDownloadDTO = BeanUtils.copyProperties(downloadDTO, TransactionOrderIncomeDownloadDTO.class);
            transactionOrderIncomeDownloadDTO.setFileName("商户交易订单明细" + timeName);
            transactionOrderIncomeDownloadDTO.setTransactionTimeType(1);
            File transactionOrderFile = transactionOrderStatService.exportIncome(transactionOrderIncomeDownloadDTO, tempFile);

            AdvanceDepositIncomeDownloadDTO advanceDepositIncomeDownloadDTO = BeanUtils.copyProperties(downloadDTO, AdvanceDepositIncomeDownloadDTO.class);
            advanceDepositIncomeDownloadDTO.setFileName("预存款收支明细" + timeName);
            File advanceDepositFile = advanceDepositStatService.exportIncome(advanceDepositIncomeDownloadDTO, tempFile);

            RefundLogDownloadDTO refundLogDownloadDTO = BeanUtils.copyProperties(downloadDTO, RefundLogDownloadDTO.class);
            refundLogDownloadDTO.setFileName("退款统计账单" + timeName);
            File refundLogFile = refundLogService.download(refundLogDownloadDTO, tempFile);


            PayCardLogDownloadDTO payCardLogDownloadDTO = BeanUtils.copyProperties(downloadDTO, PayCardLogDownloadDTO.class);
            payCardLogDownloadDTO.setFileName("购卡订单统计账单" + timeName);
            File payCardLogFile = payCardLogService.downloadPayCard(payCardLogDownloadDTO, tempFile);

            RideLogDetailsDownloadDTO rideLogDetailsDownloadDTO = BeanUtils.copyProperties(downloadDTO, RideLogDetailsDownloadDTO.class);
            rideLogDetailsDownloadDTO.setFileName("骑行订单详情账单" + timeName);
            rideLogDetailsDownloadDTO.setDateQueryType(1);
            File rideLogDetailFile = rideLogDetailsService.downloadRideLogDetail(rideLogDetailsDownloadDTO, tempFile);


            String uploadFileName = UUID.randomUUID().toString().replaceAll("-", "");
            return csvExportUtils.uploadZip(tempFile, uploadFileName,
                    depositFile,
                    transactionOrderFile,
                    advanceDepositFile,
                    refundLogFile,
                    payCardLogFile,
                    rideLogDetailFile,
                    file
            );
        } finally {
            FileUtil.del(tempFile);
        }
    }

    @Override
    public String download(DownloadAllStatDetailDTO downloadDTO) {

        boolean isAdminOrBrand = true;
        LoginInfo info = tokenService.getLoginInfo(downloadDTO.getToken());
        if (info == null) {
            info = BaseController.getLogin(ServletUtils.getRequest());
        }
        List<Role> roles = info.getRoles();
        List<String> rolesLevels = roles.stream().map(Role::getRoleLevel).collect(Collectors.toList());
        if (!rolesLevels.contains("0") && !rolesLevels.contains("1")) {
            isAdminOrBrand = false;
        }


        downloadDTO.setEndTime(DateUtils.endOfDay(downloadDTO.getEndTime()));


        //汇总数据
        TotalDetailData totalDetailData = new TotalDetailData();

        //其他收入支出数据
        Map<String, Object> otherDataMap = badOrderDao.selectOtherDataMap(
                downloadDTO.getStartTime(),
                DateUtils.getDayOnTheHour(downloadDTO.getEndTime()),
                StringUtil.getIntegerList(downloadDTO.getAccountIds()), downloadDTO.getBrandId()
        );
        totalDetailData.calculateOther(otherDataMap);

        //押金
        DepositIncomeDTO depositIncomeDTO = BeanUtils.copyProperties(downloadDTO, DepositIncomeDTO.class);
        DepositTotalIncomeVO totalIncome = depositStatService.getTotalIncome(depositIncomeDTO);
        totalDetailData.calculateDeposit(totalIncome);

        //预存款(调整成预统计查询，不从流水查询)
        AdvanceDepositTotalIncomeVO manualTotalAdvanceDeposit = changeDao.getNoManualTotalAdvanceDeposit(
                StringUtil.getIntegerList(downloadDTO.getAccountIds()),
                downloadDTO.getStartTime().getTime(),
                downloadDTO.getEndTime().getTime(),
                downloadDTO.getBrandId()
        );
        totalDetailData.calculateAdvanceDeposit(manualTotalAdvanceDeposit);

        //商户统计(公式=微信商户+支付宝商户+汇付商户)
        TransactionOrderIncomeDTO orderIncomeDTO = BeanUtils.copyProperties(downloadDTO, TransactionOrderIncomeDTO.class);
        orderIncomeDTO.setTransactionTimeType(2);
        //支付平台 1：微信，2：支付宝，3：汇付
        orderIncomeDTO.setPayPlatform(1);
        TransactionOrderTotalIncomeVO weChat = transactionOrderStatService.getTotalIncome(orderIncomeDTO);

        orderIncomeDTO.setPayPlatform(2);
        TransactionOrderTotalIncomeVO alipay = transactionOrderStatService.getTotalIncome(orderIncomeDTO);

        orderIncomeDTO.setPayPlatform(3);
        TransactionOrderTotalIncomeVO remittance = transactionOrderStatService.getTotalIncome(orderIncomeDTO);

        totalDetailData.calculateMerchant(weChat, alipay, remittance);

        //运营营收(公式=骑行费用+用户罚款+用户购卡-退款)
        RideLogDetailsQueryDTO rideLogDetailsQueryDTO = BeanUtils.copyProperties(downloadDTO, RideLogDetailsQueryDTO.class);
        rideLogDetailsQueryDTO.setDateQueryType(1);
        RideLogDetailsSumVO rideLogDetailsSum = rideLogDetailsService.sum(rideLogDetailsQueryDTO);

        PayCardLogQueryDTO payCardLogQueryDTO = BeanUtils.copyProperties(downloadDTO, PayCardLogQueryDTO.class);
        PayCardLogSumVO payCardLogSum = payCardLogService.sum(payCardLogQueryDTO);

        RefundLogSum refundLogSum = sumOpRefundLog(
                downloadDTO.getBrandId(),
                downloadDTO.getAccountIds(),
                downloadDTO.getStartTime(),
                downloadDTO.getEndTime()
        );
        totalDetailData.calculateOperation(rideLogDetailsSum, payCardLogSum, refundLogSum);

        //商户统计(公式=运营营收+预存款+押金+其他收支)
        totalDetailData.calculateMerchantStat();


        File tempFile = csvExportUtils.getTempFile();
        try {
            String timeName = "-" + DateUtil.formatDate(downloadDTO.getStartTime()) + "-" + DateUtil.formatDate(downloadDTO.getEndTime());
            String fileName = "总计表（选择所有区域）";
            if (downloadDTO.getIsAllAccount() == 2) {
                fileName = "总计表（未选择所有区域）";
            }
            File file = new File(tempFile, fileName + ".xls");

            List<String[]> rows = new ArrayList<>();

            if (downloadDTO.getIsAllAccount() == 1 && isAdminOrBrand) {
                rows.add(new String[]{"商户统计(公式=运营营收+预存款+押金+其他收支)", totalDetailData.getMerchantStat().toString()});
                rows.add(new String[]{"运营营收", totalDetailData.getOperation().toString()});
                rows.add(new String[]{"预存款", totalDetailData.getAdvanceDeposit().toString()});
                rows.add(new String[]{"押金", totalDetailData.getUserDeposit().toString()});
                rows.add(new String[]{"其他收支", totalDetailData.getOther().toString()});
            }
            rows.add(new String[]{""});

            if (isAdminOrBrand) {
                rows.add(new String[]{"商户统计(公式=微信商户+支付宝商户+汇付商户)", totalDetailData.getMerchant().toString()});
                rows.add(new String[]{"微信商户（商户交易订单明细对账单）", totalDetailData.getWechat().toString()});
                rows.add(new String[]{"支付宝商户（商户交易订单明细对账单）", totalDetailData.getAlipay().toString()});
                rows.add(new String[]{"汇付商户（商户交易订单明细对账单）", totalDetailData.getRemittance().toString()});
                rows.add(new String[]{""});
            }

            rows.add(new String[]{"运营营收(公式=骑行费用+用户罚款+用户购卡-退款)", totalDetailData.getOperation().toString()});
            rows.add(new String[]{"骑行费用（骑行订单明细对账单）", totalDetailData.getCycling().toString()});
            rows.add(new String[]{"用户罚款（骑行订单明细对账单）", totalDetailData.getUserFine().toString()});
            rows.add(new String[]{"用户购卡（购卡订单明细对账单）", totalDetailData.getPurchase().toString()});
            rows.add(new String[]{"退款（退款订单明细对账单）", totalDetailData.getRefund().toString()});
            rows.add(new String[]{""});

            rows.add(new String[]{"预存款", totalDetailData.getAdvanceDeposit().toString()});
            rows.add(new String[]{"预存款交易收入（预存款收支明细对账单）", totalDetailData.getAdvanceDepositIncome().toString()});
            rows.add(new String[]{"预存款交易支出（预存款收支明细对账单）", totalDetailData.getAdvanceDepositExpenses().toString()});
            rows.add(new String[]{""});

            rows.add(new String[]{"押金", totalDetailData.getUserDeposit().toString()});
            rows.add(new String[]{"押金收入（押金收支明细对账单）", totalDetailData.getUserDepositIncome().toString()});
            rows.add(new String[]{"押金支出（押金收支明细对账单）", totalDetailData.getUserDepositExpenses().toString()});
            rows.add(new String[]{""});

            if (isAdminOrBrand) {
                rows.add(new String[]{"其他收支", totalDetailData.getOther().toString()});
                rows.add(new String[]{"其他收入", totalDetailData.getOtherIncome().toString()});
                rows.add(new String[]{"其他支出", totalDetailData.getOtherExpenses().toString()});
                rows.add(new String[]{""});
            }


            if (downloadDTO.getIsAllAccount() == 1) {
                rows.add(new String[]{"注：预存款收支不包含手工修改预存款金额"});
            } else {
                if (isAdminOrBrand) {
                    rows.add(new String[]{"注1：未勾选所有区域，商户统计≠运营营收+预存款+押金+其他收支"});
                    rows.add(new String[]{"注2：预存款收支不包含手工修改预存款金额"});
                    rows.add(new String[]{"注3：退款订单明细只包含骑行费用、用户罚款、用户购卡退款，不包含押金、预存款等退款"});
                } else {
                    rows.add(new String[]{"注1：预存款收支不包含手工修改预存款金额"});
                    rows.add(new String[]{"注2：退款订单明细只包含骑行费用、用户罚款、用户购卡退款，不包含押金、预存款等退款"});
                }
            }
            ExportExcelUtil.exportList(fileName, rows, file);

            //押金收支明细
            DepositIncomeDownloadDTO depositIncomeDownloadDTO = BeanUtils.copyProperties(downloadDTO, DepositIncomeDownloadDTO.class);
            depositIncomeDownloadDTO.setFileName("押金收支明细" + timeName);
            File depositFile = depositStatService.exportIncome(depositIncomeDownloadDTO, tempFile);

            //商户交易订单明细
            TransactionOrderIncomeDownloadDTO transactionOrderIncomeDownloadDTO = BeanUtils.copyProperties(downloadDTO, TransactionOrderIncomeDownloadDTO.class);
            transactionOrderIncomeDownloadDTO.setFileName("商户交易订单明细" + timeName);
            transactionOrderIncomeDownloadDTO.setTransactionTimeType(2);
            File transactionOrderFile = transactionOrderStatService.exportIncome(transactionOrderIncomeDownloadDTO, tempFile);

            //预存款收支明细
            File advanceDepositFile = exportNoManualAdvanceDeposit(
                    StringUtil.getIntegerList(downloadDTO.getAccountIds()),
                    downloadDTO.getStartTime(),
                    downloadDTO.getEndTime(),
                    downloadDTO.getBrandId(),
                    tempFile,
                    "预存款收支明细" + timeName
            );
            //退款统计账单
            File refundLogFile = exportOpRefund(
                    downloadDTO.getAccountIds(),
                    downloadDTO.getStartTime(),
                    downloadDTO.getEndTime(),
                    downloadDTO.getBrandId(),
                    tempFile,
                    "退款统计账单" + timeName
            );
            //购卡订单统计账单
            PayCardLogDownloadDTO payCardLogDownloadDTO = BeanUtils.copyProperties(downloadDTO, PayCardLogDownloadDTO.class);
            payCardLogDownloadDTO.setFileName("购卡订单统计账单" + timeName);
            File payCardLogFile = payCardLogService.downloadPayCard(payCardLogDownloadDTO, tempFile);

            //骑行订单详情账单
            RideLogDetailsDownloadDTO rideLogDetailsDownloadDTO = BeanUtils.copyProperties(downloadDTO, RideLogDetailsDownloadDTO.class);
            rideLogDetailsDownloadDTO.setFileName("骑行订单详情账单" + timeName);
            rideLogDetailsDownloadDTO.setDateQueryType(1);
            File rideLogDetailFile = rideLogDetailsService.downloadRideLogDetail(rideLogDetailsDownloadDTO, tempFile);


            String uploadFileName = UUID.randomUUID().toString().replaceAll("-", "");

            if (isAdminOrBrand) {
                return csvExportUtils.uploadZip(tempFile, uploadFileName,
                        depositFile,
                        transactionOrderFile,
                        advanceDepositFile,
                        refundLogFile,
                        payCardLogFile,
                        rideLogDetailFile,
                        file);
            } else {
                return csvExportUtils.uploadZip(tempFile, uploadFileName,
                        depositFile,
                        advanceDepositFile,
                        refundLogFile,
                        payCardLogFile,
                        rideLogDetailFile,
                        file);
            }
        } finally {
            FileUtil.del(tempFile);
        }
    }


    public RefundLogSum sumOpRefundLog(Integer brandId, String accountIds, Date startTime, Date endTime) {
        //现金交易退款总金额
        Long cashRefundSumMoney = 0L;
        //现金交易退款总笔数
        Integer cashRefundSumCount = 0;
        //虚拟费用退款总金额
        Long virtualRefundSumMoney = 0L;
        //虚拟费用退款总笔数
        Integer virtualRefundSumCount = 0;
        Map<Integer, RefundLog> refundLogMap = refundLogDao.sumOpRefundLog(brandId, accountIds, startTime, endTime);
        for (Map.Entry<Integer, RefundLog> entry : refundLogMap.entrySet()) {
            RefundLog refundLog = JsonUtils.json2Obj(JsonUtils.obj2Json(entry.getValue()), RefundLog.class);
            switch (entry.getKey()) {
                //虚拟退款的类型
                case RefundLogConstant.MANUAL_MODIFICATION_OF_THE_ORDER_TO_OFFSET_THE_COST_REFUND:
                case RefundLogConstant.REFUND_OF_OTHER_VIRTUAL_DEDUCTIBLE_EXPENSES:
                    virtualRefundSumMoney += refundLog == null ? 0L : refundLog.getChangeMoney();
                    virtualRefundSumCount += refundLog == null ? 0 : refundLog.getRefundId().intValue();
                    break;
                default:
                    cashRefundSumMoney += refundLog == null ? 0L : refundLog.getChangeMoney();
                    cashRefundSumCount += refundLog == null ? 0 : refundLog.getRefundId().intValue();
                    break;
            }
        }
        return new RefundLogSum(((double) cashRefundSumMoney / 100D), cashRefundSumCount, ((double) virtualRefundSumMoney / 100D), virtualRefundSumCount);
    }


    /**
     * 导出无人工预存款列表
     *
     * @param accountIds
     * @param startTime
     * @param endTime
     * @param brandId
     * @param tempFile
     * @param fileName
     * @return
     */
    public File exportNoManualAdvanceDeposit(List<Integer> accountIds, Date startTime, Date endTime, Integer brandId, File tempFile, String fileName) {
        //自定义导出处理器
        ExportHandler<AdvanceDepositVO> exportHandler = new ExportHandler() {
            @Override
            public List handle(CsvBaseEntity baseEntity) {
                List<AdvanceDepositVO> noManualAdvanceDepositList = changeDao.getNoManualAdvanceDepositList(
                        accountIds,
                        baseEntity.getStartTime().getTime(),
                        baseEntity.getEndTime().getTime(),
                        brandId);
                Map<Integer, String> accountMap = accountDao.getByAccountIds(accountIds).stream().collect(Collectors.toMap(Account::getAccountId, Account::getName));
                noManualAdvanceDepositList.forEach(v -> {
                    v.setAccountName(accountMap.get(v.getAccountId()));
                });
                return noManualAdvanceDepositList;
            }
        };
        //多线程导出csv
        return csvExportUtils.exportBigCsv(exportHandler, AdvanceDepositVO.class, startTime, endTime, tempFile, fileName);
    }


    /**
     * 导出运营营收的退款详情
     *
     * @param accountIds
     * @param startTime
     * @param endTime
     * @param brandId
     * @param tempFile
     * @param fileName
     * @return
     */
    public File exportOpRefund(String accountIds, Date startTime, Date endTime, Integer brandId, File tempFile, String fileName) {
        return csvExportUtils.exportBigCsv((baseEntity) -> {
            List<RefundLog> list = refundLogDao.getOpRefundList(
                    accountIds,
                    baseEntity.getStartTime(),
                    baseEntity.getEndTime(),
                    brandId);
            return list;
        }, RefundLog.class, startTime, endTime, tempFile, fileName);
    }
}
